﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using MethodWorx.Core.DomainModel;

namespace MethodWorx.CMS.Workflow
{
    public class WorkflowExecutionEngine
    {
        private IWorkflowExecutionContext context;
        private WorkflowConfiguration configuration;
        private IEnumerable<IWorkflowComponent> components;

        public WorkflowExecutionEngine(IEnumerable<IWorkflowComponent> components, IWorkflowExecutionContext context, WorkflowConfiguration configuration)
        {
            this.context = context;
            this.configuration = configuration;
            this.components = components;
        }

        public void Execute()
        {
            using (var uow = this.context.CmsDataProvider.GetUnitOfWorkFactory().Begin())
            {
                var step = this.configuration.StartingStep;
                if (step != null)
                {
                    this.ExecuteStep(step, uow);
                }
                if(uow.IsOpen)
                    uow.Commit();
            }
        }

        private void ExecuteStep(WorkflowConfigurationStep step, IUnitOfWork uow)
        {
            //
            //  if we are a branch step, then we automatically fall to the next step (its first child)
            if (step is DefaultBranchStep || step is BranchStep)
                step = (step.Steps ?? new WorkflowConfigurationStep [] { }).FirstOrDefault();

            if (step != null)
            {
                var component = this.components.FirstOrDefault(c => c.Id == step.ComponentId);
                if (component != null)
                {
                    //  execute the step
                    var result = component.Execute(this.context, step.ComponentConfiguration);
                    if (result == null)
                        throw new Exception("Uknown Workflow Result");
                    else if (!string.IsNullOrEmpty(result.Message))
                        throw new Exception("Worfklow Error: " + result.Message);
                    else if(result.OnFinish != null)
                    {
                        uow.Commit();
                        result.OnFinish();
                    }
                    else if (result.StepCompleted)
                    {
                        if (step.Output != null)
                            this.context.AddWorkflowValue(step.Output,
                                                          result.Output == null ? null : result.Output);
                        //  we are completed
                        if (step.Steps != null)
                        {
                            var nextStep = step.Steps.Skip(result.ExitBranch).Take(1).FirstOrDefault();
                            if (nextStep != null)
                                this.ExecuteStep(nextStep, uow);
                        }
                    }
                }
            }
        }
    }
}
